package club.justwill.single.Service.impl;

import club.justwill.single.Service.InvitationService;
import club.justwill.single.Service.OrderService;
import club.justwill.single.Service.UserService;
import club.justwill.single.assembler.ProjectAssemble;
import club.justwill.single.assembler.UserAdviceAssemble;
import club.justwill.single.assembler.UserAssemble;
import club.justwill.single.bean.*;
import club.justwill.single.bean.WeiChat.WeiChatOpenId;
import club.justwill.single.helper.FileHelper;
import club.justwill.single.impl.ToolsImpl;
import club.justwill.single.model.*;
import club.justwill.single.persistence.dao.ProjectDao;
import club.justwill.single.persistence.dao.UserAliaseDao;
import club.justwill.single.persistence.dao.UserDao;
import club.justwill.single.persistence.document.*;
import club.justwill.single.persistence.jpo.Projects;
import club.justwill.single.persistence.jpo.User;
import club.justwill.single.persistence.jpo.UserAliases;
import club.justwill.single.persistence.store.*;
import club.justwill.single.util.HttpJson;
import club.justwill.single.util.JsonResponseHelper;
import club.justwill.single.util.ShareCodeUtil;
import com.google.gson.Gson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Created by xiangnan on 15-12-24.
 */
@Service
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class UserServiceImpl extends BaseService implements UserService {

    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    @Autowired
    private UserDao userDao;

    @Autowired
    private InvitationService invitationService;

    @Autowired
    private UserAliaseDao userAliaseDao;

    @Autowired
    private SessionDataStore sessionDataStore;

    @Autowired

    private MobileAuthCodeStore mobileAuthCodeStore;

    @Autowired
    private UserAddressStore userAddressStore;

    @Autowired
    private UserFollowProjectStore userFollowProjectStore;

    @Autowired
    private ProjectDao projectDao;

    @Autowired
    private ProjectAssemble projectAssemble;

    @Autowired
    private UserAssemble userAssemble;

    @Autowired
    private FileHelper fileHelper;

    @Autowired
    private UserSecurityCodeStore userSecurityCodeStore;

    @Autowired
    private UserSupportProjectStore userSupportProjectStore;

    @Autowired
    private OrderService orderService;

    @Autowired
    private UserAdviceStore userAdviceStore;

    @Autowired
    private UserAdviceAssemble userAdviceAssemble;
    @Override
    @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Throwable.class, readOnly = true)
    public User authenticate(SecurityToken token,SessionState sessionState, UserLogin userLogin, JsonResponse obj) {
        User user = userDao.getByUserName(userLogin.getUsername());
        if (user == null) {
            JsonResponseHelper.addError(obj, CoreConstants.USER_NOT_EXIST);
            return user;
        } else if (!ToolsImpl.isValidPassword(userLogin.getPassword(), user.getPassword())) {
            JsonResponseHelper.addError(obj, CoreConstants.PASSWORD_ERROR);
            return user;
        }
        SessionData data = new SessionData();
        data.setUserId(user.getUserId());
        data.setUserType(user.getType());
        data.setSessionId(sessionState.getSessionId());
        sessionDataStore.save(data);
        sessionDataStore.saveOrUpdate(data);
        return user;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Throwable.class, readOnly = false)
    public void exLogIn(SecurityToken token, ExLogIn exLogIn, JsonResponse obj, SessionState sessionState){
        if( exLogIn.getType() != UserAliaseType.WEIBO && exLogIn.getType() != UserAliaseType.WEIXIN) {
            JsonResponseHelper.addError(obj, CoreConstants.Aliase_Type_Error);
            return;
        }
        User user = userDao.getByUserName(exLogIn.getUsername());
        if (user == null) {
            UserAliases userAliases = new UserAliases();
            userAliases.setAliasName(exLogIn.getUsername());
            userAliases.setType(exLogIn.getType());
            user = new User();
            user.setNickName(exLogIn.getUsername());
            user.setPassword(CoreConstants.DEFAULT_PASSWORD);
            user.setSex(CoreConstants.UNKNOWN);
            user.setType(UserType.INDIVIDUAL);
            user.setStatus(CoreConstants.ACTIVE);
            user.setCreationTime(new Timestamp(System.currentTimeMillis()));
            user.setProcessChannel(token.getChannel());
            userDao.save(user, token);
            userAliases.setUserId(user.getUserId());
            userAliaseDao.save(userAliases, token);
        } else {
            JsonResponseHelper.addError(obj, CoreConstants.USER_ALREADY_EXISTS);
        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Throwable.class, readOnly = false)
    public String uploadHeadIcon(SecurityToken token, JsonResponse obj, CommonsMultipartFile headIcon) {
        String path = fileHelper.saveFile(headIcon);
        if (path == null || "".equals(path)) {
            JsonResponseHelper.addError(obj, CoreConstants.FILE_UPLOAD_FAILED);

        } else {
            User user = userDao.getById(token.getOperatorUserId());
            if (user == null) {
                JsonResponseHelper.addError(obj, CoreConstants.USER_NOT_EXIST);
            } else {
                user.setHeadIcon(path);
                userDao.update(user, token);
                return path;
            }
        }
        return null;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Throwable.class, readOnly = false)
    public void setSecurityCode(SecurityToken token, JsonResponse obj, SecurityCode securityCode) {
        if (!securityCode.getSecurityCode().equals(securityCode.getConfirmCode())) {
            JsonResponseHelper.addError(obj, CoreConstants.SECURITY_CODE_NOT_SAME);
        } else if (securityCode.getSecurityCode().length() < 6 || securityCode.getSecurityCode().length() > 20) {
            JsonResponseHelper.addError(obj, CoreConstants.SECURITY_LENGTH_ERROR);
        } else {
            UserSecurityCode userSecurityCode = userSecurityCodeStore.searchSecurityCode(token.getOperatorUserId());
            if (userSecurityCode == null) {
                userSecurityCode = new UserSecurityCode();
                userSecurityCode.setUserId(token.getOperatorUserId());
                userSecurityCode.setSecurityCode(securityCode.getSecurityCode());
                userSecurityCodeStore.save(userSecurityCode);
            } else {
                userSecurityCodeStore.remove(userSecurityCode);
                userSecurityCode.setUserId(token.getOperatorUserId());
                userSecurityCode.setSecurityCode(securityCode.getSecurityCode());
                userSecurityCodeStore.save(userSecurityCode);
            }

        }
    }
    @Override
    @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Throwable.class, readOnly = false)
    public UserSecurityCode searchSecurityCode(long userId) {
        UserSecurityCode userSecurityCode = userSecurityCodeStore.searchSecurityCode(userId);
        return userSecurityCode;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Throwable.class, readOnly = false)
    public User getUserById(JsonResponse obj, long userId) {
        User user = userDao.getById(userId);
        return user;
    }




    @Override
    @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Throwable.class, readOnly = false)
    public void signUp(SecurityToken token, UserSignUp userSignUp, JsonResponse obj, SessionState sessionState) {
        if (!ToolsImpl.isValidMobile(userSignUp.getMobile())) {
            JsonResponseHelper.addError(obj, CoreConstants.MOBILE_ERROR);
            return ;
        } else if (!ToolsImpl.isQualifiedPassword(userSignUp.getPassword())) {
            JsonResponseHelper.addError(obj, CoreConstants.PASSWORD_NOT_QUALIFIED);
            return ;
        } else {
            boolean authCodeCorrect = false;
            List<MobileAuthCode> authCodeList = mobileAuthCodeStore.searchCodesByMobile(userSignUp.getMobile());
            if(ToolsImpl.isEmpty(authCodeList)) {
                JsonResponseHelper.addError(obj,CoreConstants.AUTH_CODE_ERROR);
            }else {
                for(MobileAuthCode code : authCodeList) {
                    if(code.getAuthcode() != userSignUp.getAuthCode()){
                        continue;
                    } else  {
                        authCodeCorrect = true;
                        User user = userDao.getByUserName(userSignUp.getMobile());
                        if(user == null){
                            UserAliases userAliases = new UserAliases();
                            userAliases.setAliasName(userSignUp.getMobile());
                            userAliases.setType(UserAliaseType.PHONENUMBER);
                            user = new User();
                            user.setPassword(ToolsImpl.hashPasswordAddingSalt(userSignUp.getPassword()));
                            user.setNickName("User:"+userSignUp.getMobile());
                            user.setSex(CoreConstants.UNKNOWN);
                            user.setHeadIcon(CoreConstants.DEFAULT_HEAD_ICON);
                            user.setType(UserType.INDIVIDUAL);
                            user.setStatus(CoreConstants.ACTIVE);
                            user.setCreationTime(new Timestamp(System.currentTimeMillis()));
                            user.setInvitationCode(ShareCodeUtil.getCode());
                            user.setProcessChannel(token.getChannel());
                            user.setBalance(0L);
                            userDao.save(user, token);
                            userAliases.setUserId(user.getUserId());
                            userAliaseDao.save(userAliases, token);
                            authCodeCorrect = true;
                        } else {
                            JsonResponseHelper.addError(obj,CoreConstants.USER_ALREADY_EXISTS);
                        }
                    }
                }
                if(!authCodeCorrect){
                    JsonResponseHelper.addError(obj,CoreConstants.AUTH_CODE_ERROR);
                }
            }
            /*//获取注册时的邀请码
            String invitationCode = userSignUp.getInvitationCode();
            //如果邀请码不为空
            if (!ToolsImpl.isEmpty(invitationCode)) {
                //获取该邀请码对应的用户
                User inviter = userDao.getByInvitationCode(invitationCode);
                if(inviter == null) {
                    JsonResponseHelper.addError(obj, CoreConstants.INVALID_INVITATION_CODE);
                } else {
                    AddInvitation addInvitation = new AddInvitation();
                    addInvitation.setInviterId(inviter.getUserId());
                    addInvitation.setInviteeId(user.getUserId());
                    invitationService.addInvitation(token, addInvitation, obj, sessionState);
                }
            }
*/
        }

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Throwable.class, readOnly = false)
    public void saveAlipayNumber(JsonResponse obj, SecurityToken token, String alipayNumber) {
        User user = userDao.getById(token.getOperatorUserId());
        if (user == null) {
            JsonResponseHelper.addError(obj, CoreConstants.USER_NOT_EXIST);
        } else {
            UserAliases userAliases = new UserAliases();
            userAliases.setUserId(user.getUserId());
            userAliases.setAliasName(alipayNumber);
            userAliases.setType(UserAliaseType.ALIPAY);
            userAliaseDao.save(userAliases, token);
        }

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Throwable.class, readOnly = false)
    public void linkWeiXin(JsonResponse obj, SecurityToken token, String code, Long userId) {
        UserAliases userAliases = userAliaseDao.getByUserIdAndType(userId, UserAliaseType.WEIXIN);
        if (userAliases == null) {
            String appid = WeixinConfig.appid;
            String secret = WeixinConfig.secret;
            String grant_type = WeixinConfig.grant_type;
            StringBuffer accessTokenUrl = new StringBuffer(WeixinConfig.ACCESS_TOKEN_URL);
            String url = accessTokenUrl.
                    append("&appid=").append(appid).
                    append("&secret=").append(secret).
                    append("&code=").append(code).
                    append("&grant_type=").append(grant_type).toString();
            String result = HttpJson.httpGet(url);
            logger.debug("*********************************************************************************************"+result);
            if (!result.contains("err")) {
                Gson gson = new Gson();
                WeiChatOpenId weiChatOpenId = gson.fromJson(result, WeiChatOpenId.class);
                userAliases = new UserAliases();
                userAliases.setUserId(userId);
                userAliases.setType(UserAliaseType.WEIXIN);
                System.out.println(result);
                userAliases.setAliasName(weiChatOpenId.getOpenid());
                userAliaseDao.save(userAliases,token);
            } else {
                JsonResponseHelper.addError(obj, CoreConstants.GET_WEIXIN_TOKEN_ERROR);
            }
        } else {
            JsonResponseHelper.addError(obj,CoreConstants.WEIXIN_ALREADY_LINKED);
        }

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Throwable.class, readOnly = false)
    public void collectAdvice(JsonResponse obj, long userId, CollectAdvice collectAdvice) {
        if (collectAdvice.getType() ==0 || collectAdvice.getType()==10){
            User user = userDao.getById(userId);
            if (user==null) {
                JsonResponseHelper.addError(obj, CoreConstants.USER_NOT_EXIST);
                return;
            }
                UserAdvice userAdvice = new UserAdvice();
                userAdvice.setUserId(userId);
                userAdvice.setPhoneNumber(collectAdvice.getPhoneNumber());
                userAdvice.setUserName(user.getNickName());
                userAdvice.setType(collectAdvice.getType());
                userAdvice.setContent(collectAdvice.getContent());
                userAdviceStore.save(userAdvice);
        } else {
            JsonResponseHelper.addError(obj, CoreConstants.ADVICE_TYEE_ERROR);
        }

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Throwable.class, readOnly = false)
    public List<UserAdviceResp> searchAdvice(JsonResponse obj, int index, int size) {
        if (index<0) {
            JsonResponseHelper.addError(obj, CoreConstants.INDEX_ERROR);
        } else if (size<=0){
            JsonResponseHelper.addError(obj, CoreConstants.SIZE_ERROR);
        } else {
            List<UserAdvice> userAdviceList = userAdviceStore.getByPage(index, size);
            List<UserAdviceResp> userAdviceRespList = userAdviceAssemble.assebmleUserAdviceList(userAdviceList);
            return userAdviceRespList;

        }
        return new ArrayList<UserAdviceResp>();

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Throwable.class, readOnly = false)
    public List<UserResp> getUsers(JsonResponse obj, SecurityToken token, int index, int size) {
        List<User> users = userDao.searchUsers(index,size);
        List<UserResp> userResps = new ArrayList<UserResp>();
        if(!ToolsImpl.isEmpty(users)){
            for(User user:users){
                userResps.add(userAssemble.assembleUser(user));
            }
            return userResps;
        }else{
            return null;
        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Throwable.class, readOnly = false)
    public void resetPassword(SecurityToken token, UserChangePassword userChangePassword, JsonResponse obj, SessionState sessionState) {
        if (!ToolsImpl.isValidMobile(userChangePassword.getPhoneNumber())) {
            JsonResponseHelper.addError(obj, CoreConstants.MOBILE_ERROR);
            return;
        } else if (!ToolsImpl.isQualifiedPassword(userChangePassword.getNewPassword())) {
            JsonResponseHelper.addError(obj, CoreConstants.PASSWORD_NOT_QUALIFIED);
            return;
        } else if (sessionState.getAuthCode() != userChangePassword.getAuthCode() || sessionState.isAutoCodeExpired()) {
            JsonResponseHelper.addError(obj, CoreConstants.AUTH_CODE_ERROR);
            return;
        }  else {
            User user = userDao.getByUserName(userChangePassword.getPhoneNumber());
            if (user == null) {
                JsonResponseHelper.addError(obj, CoreConstants.USER_NOT_EXIST);
            } else {
                user.setPassword(ToolsImpl.hashPasswordAddingSalt(userChangePassword.getNewPassword()));
            }

        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Throwable.class, readOnly = false)
    public SessionData isLoginIn(String single) {
        SessionData data = null;
        if(single !=null && !"".equals(single)){
            long uuid = Long.parseLong(single);
            data = sessionDataStore.isLoginIn(uuid);
        }
        return data;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Throwable.class, readOnly = false)
    public SessionData isAppLogIn(String jsessionid) {
        SessionData data = null;
        if(jsessionid !=null && !"".equals(jsessionid)){
            data = sessionDataStore.isAppLogIn(jsessionid);
        }
        return data;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Throwable.class, readOnly = false)
    public void followProject(SecurityToken token, JsonResponse obj, long projectId) {
        UserFollowProject userFollowProject = userFollowProjectStore.searchFollowedProject(token.getOperatorUserId());
        if(userFollowProject == null){
            userFollowProject = new UserFollowProject();
            userFollowProject.setUserId(token.getOperatorUserId());
        }
        List<Long> followedProjectIdList = userFollowProject.getFollowedProjectIdList();
        boolean followed = false;
        for(Long id : followedProjectIdList) {
            if(id == projectId){
                JsonResponseHelper.addError(obj, CoreConstants.PROJECT_ALREADY_FOLLOWED);
                followed = true;
            }
        }
        if(!followed){
            followedProjectIdList.add(projectId);
            userFollowProjectStore.save(userFollowProject);
        }

    }
    @Override
    @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Throwable.class, readOnly = false)
    public void unFollowProject(SecurityToken token, JsonResponse obj, long projectId) {
        UserFollowProject userFollowProject = userFollowProjectStore.searchFollowedProject(token.getOperatorUserId());
        if(userFollowProject == null) {
            JsonResponseHelper.addError(obj, CoreConstants.USER_NOT_FOLLOW_PROJECT);
        } else if(!userFollowProject.getFollowedProjectIdList().contains(projectId)){
            JsonResponseHelper.addError(obj, CoreConstants.USER_NOT_FOLLOW_PROJECT);
        } else {
            userFollowProjectStore.updateFollowedProject(token.getOperatorUserId(),projectId);
        }
    }

    @Override
    public void logout(JsonResponse obj, SecurityToken token) {
        SessionData data = sessionDataStore.getByUserId(token.getOperatorUserId());
        if (data == null) {
            JsonResponseHelper.addError(obj, CoreConstants.LOG_OUT_ERROR);
        }
        sessionDataStore.remove(data);
    }



    @Override
    @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Throwable.class, readOnly = false)
    public List<ProjectsResp> searchFollowedProject(SecurityToken token, JsonResponse obj, SearchProjects searchProjects) {
        UserFollowProject userFollowProject = userFollowProjectStore.searchFollowedProject(token.getOperatorUserId());
        if(userFollowProject == null) {
            return new ArrayList<ProjectsResp>();
        } else {
            List<Long> followedProjectIdList = userFollowProject.getFollowedProjectIdList();
            List<Projects> projectList = projectDao.searchProjectsByIdList(searchProjects.getIndex(), searchProjects.getSize(), followedProjectIdList);
            if(!ToolsImpl.isEmpty(projectList)){
                return projectAssemble.assembleProjectMiniLisit(projectList);
            }
        }
        return new ArrayList<ProjectsResp>();
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Throwable.class, readOnly = false)
    public List<ProjectsResp> searchSupportedProject(SecurityToken token, JsonResponse obj , SearchProjects searchProjects) {
        User user = userDao.getById(token.getOperatorUserId());
        if (user == null) {
            JsonResponseHelper.addError(obj, CoreConstants.USER_NOT_EXIST);
        }
        List<OrdersResp> orderList = orderService.getOrders(token, obj);
        Set<Long> projectIdSet = new HashSet<Long>();
        for (OrdersResp or : orderList) {
            Projects pro = projectDao.getByOrderId(Long.parseLong(or.getOrderId()));
            projectIdSet.add(pro.getProjectId());
        }
        List<Long> projectIdList = new ArrayList<Long>(projectIdSet);
        List<ProjectsResp> projectsRespList = new ArrayList<ProjectsResp>();
        for (Long projectId : projectIdList) {
            List<OrdersResp> orderResList = orderService.getByUidAndPid(searchProjects, user.getUserId(), projectId);
            ProjectsResp projectsResp = projectAssemble.assembleProjectMini(projectDao.getByProjectId(projectId));
            projectsResp.setOrdersRespList(orderResList);
            projectsRespList.add(projectsResp);
        }
        return projectsRespList;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Throwable.class, readOnly = false)
    public List<User> searchCommercial(SecurityToken token, JsonResponse obj, int index, int size) {
        List<User> users = userDao.searchCommercial(index,size);

        return null;
    }

    @Override
    public List<UserAddress> getAddress(SecurityToken token, JsonResponse obj) {
        User user = userDao.getById(token.getOperatorUserId());
        List<UserAddress> userAddresses = null;
        if(user == null) {
            JsonResponseHelper.addError(obj, CoreConstants.USER_NOT_EXIST);
        } else {
            userAddresses = userAddressStore.getAddByUserId(user.getUserId());
        }
        return userAddresses;
    }

    @Override
    public void updateAddress(SecurityToken token, JsonResponse obj, UpdateUserAddress updateUserAddress) {
        UserAddress userAddress = userAddressStore.getAddByAddId(updateUserAddress.getAddressId());
        if(userAddress == null) {
            JsonResponseHelper.addError(obj, CoreConstants.ADDRESS_IS_NULL);
        } else {
            userAddress.setName(updateUserAddress.getName());
            userAddress.setProvince(updateUserAddress.getProvince());
            userAddress.setCity(updateUserAddress.getCity());
            userAddress.setAddress(updateUserAddress.getAddress());
            userAddress.setPhoneNumber(updateUserAddress.getPhone());
            userAddressStore.save(userAddress);
        }
    }

    @Override
    public void addAddress(SecurityToken token, JsonResponse obj, AddUserAddress addUserAddress) {
        User user = userDao.getById(token.getOperatorUserId());
        if(user == null) {
            JsonResponseHelper.addError(obj, CoreConstants.PLEASE_LOGIN);
        } else {
            UserAddress userAddress = new UserAddress();
            userAddress.setName(addUserAddress.getName());
            userAddress.setProvince(addUserAddress.getProvince());
            userAddress.setCity(addUserAddress.getCity());
            userAddress.setAddress(addUserAddress.getAddress());
            userAddress.setPhoneNumber(addUserAddress.getPhone());
            userAddress.setUserId(user.getUserId());
            userAddressStore.save(userAddress);
        }

    }

    @Override
    public void deleteAddress(SecurityToken token, JsonResponse obj, String AddressId) {
        UserAddress userAddress = userAddressStore.getAddByAddId(AddressId);
        if(userAddress != null) {
            userAddressStore.remove(userAddress);
        }
    }
}
